We show how to use the branch-and-bound design strategy by applying it to the 0-1 Knapsack problem. First we discuss a simple version called breadth-first search with branch-and-bound pruning. After that, we show an improvement on the simple version called best-first search with branch-and-bound pruning.
0-1 배낭 문제에 너비우선탐색을 적용해봄으로써 분기한정기법을 어떻게 사용하는지 살펴볼 것이다. 그 후 동 문제에 최고우선탐색까지 적용할 것이다(너비우선탐색을 조금 변형하면 최고우선탐색이 되기 때문).
Example
Suppose that n = 4, W = 16, and we have the following:
i | $p_i$ | $w_i$ | $p_i / w_i$ |
---|---|---|---|
1 | $40 | 2 | $20 |
2 | $30 | 5 | $6 |
3 | $50 | 10 | $5 |
4 | $10 | 5 | $2 |
각 노드에는 3가지 값들이 들어가 있는데 제일 위부터 profit, weight, bound 값이다. 하늘색으로 칠해진 노드가 최적의 해답을 보유한 노드이다. 백트래킹에서 본 것과 비슷하지만 상태공간트리를 방문하는 순서가 너비우선탐색이다. 예를 들어 백트래킹기법에서는 노드(1, 2)가 유망하지 않음을 알았고 그 아래로 확장하지 않았다. 하지만 너비우선탐색을 통한 분기한정법에서는 노드(1,2)아래로 확장한다.
The Breadth-First Search with Branch-and-Bound Pruning Algorithm for the 0-1 Knapsack Problem
Algorithm Design
Using breadth-first search with branch-and-bound pruning, we proceed exactly as we did using backtracking, except that we do a breadth-first search instead of a depth-first search. That is, we let weightand profit be the total weight and total profit of the items that have been included up to a node.
Unlike depth-first search, there is no simple recursive algorithm for breadth-first search. However, we can implement it using a queue. The algorithm that follows does this. The algorithm is written specifically for trees because presently we are interested only in trees. We insert an item at the end of the queue with a procedure called enqueue, and we remove an item from the front with a procedure called dequeue.
Notice that nodes (3, 1) and (4, 3) have bounds of 0. A branch-and-bound algorithm decides whether to expand beyond a node by checking whether its bound is better than the value of the best solution found so far. Therefore, when a node is nonpromising because its weight is not less than W, we set its bound to 0. In this way, we ensure that its bound cannot be better than the value of the best solution found so far.
0-1 배낭 문제에 분기한정 가지치기 너비우선탐색을 사용하면 깊이우선탐색 대신 너비우선탐색을 하게 된다. 너비우선탐색은 깊이우선탐색과 달리 재귀 알고리즘을 사용하지 않으며, 그 대신 큐(queue) 자료구조를 사용하여 알고리즘을 구현한다. 이런 점을 제외하면 백트래킹에서 봤던 방법과 똑같은 방법으로 진행된다.
분기한정 알고리즘은 노드의 확장 여부를 결정할 때, 자식노드로 확장했을 때의 bound 값이 현재까지 구한 가장 좋은 답보다 더 큰지 검사한다. 여기서 weight가 W를 초과하여 해당 노드가 유망하지 않게 되면 노드(3, 1), (4, 3) 처럼 bound를 0으로 설정한다.
Last of all, in a simple breadth-first search with branch-and-bound pruning, the decision of whether or not to visit a node’s children is made at the time the node is visited. That is, if the branches to the children are pruned, they are pruned when the node is visited. Therefore, when we visit node (2, 3), we decide to visit its children because the value of maxprofit at that time is only 70, whereas the bound for the node is 82. Unlike a depth-first search, in a breadth-first search the value of maxprofit can change by the time we actually visit the children. In this case, maxprofit has a value of 90 by the time we visit the children of node (2, 3). We then waste our time checking these children. We avoid this in our best-first search, which is described in the next subsection.
마지막으로 분기한정 가지치기 너비우선탐색은 어떤 노드의 자식노드를 방문해야 할지는 그 노드를 방문할 때 결정한다. 깊이우선탐색과는 달리 너비우선탐색에서는 maxprofit의 값이 자식노드를 실제 방문하는 때에 변할 수 있다.
To determine whether the node is promising, we initialize totweight and bound to weight and profit, respectively, and then greedily grab items, adding their weights and profits to totweight and bound, until we reach an item whose weight would bring totweight above W . We grab the fraction of that item allowed by the available weight, and add the profit of that fraction to bound. In this way, bound becomes an upper bound on the amount of profit we could obtain by expanding beyond the node. If the node is at level i, and the node at level k is the one whose weight would bring the weight above W , then
$$
\begin{align}
totweight &= weight + \sum_{j=i+1}^{k-1}w_j \\
bound &= \left(profit + \sum_{j=i+1}^{k-1}p_j\right) + (W - totweight) \times \frac{w_k}{p_k}
\end{align}
$$
- profit: the sum of the profits of the items included up to the node.
- weight: the sum of the weights of those items.
- totweight: the sum of the weights could be obtained by expanding beyond that node (W를 초과할 수 없다.)
- bound: the upper bound on the profit that could be obtained by expanding beyond that node.
Recall that a node is also nonpromising if
$$
weight <= W
$$
Pseudo Code
void knapsack2(int n, const int p[], cont int w[], int W, int& maxprofit) |
Source Code
// File: knapsack_bfs.h |
// File: knapsack_bfs.cpp |
// File: maxtotprofit.cpp |
Time Complexity Analysis
Worst-Case Time Complexity
- $O(n) = 1 + 2 + 2^2 + 2^3 + … + 2^n = 2^{(n+1)} - 1 \in \Theta(2^n)$
$w_i$를 포함시키느냐 그렇지 않느냐의 두 가지 선택이므로 상태공간트리 내 전체 노드의 수는 최대 $2^{n+1} - 1$개가 된다. 최악의 경우 방문하는 노드의 수가 지수이지만, 가지치기를 통해 상태노드의 수를 줄일 수 있기 때문에 알고리즘의 효율은 더 좋을 수 있다.
Comparing the Algorithm Techniques for the 0-1 Knapsack Problem
Problem | Algoritm Technique | Worst-Case Time Complexity |
---|---|---|
0-1 Knapsack Problem | Dynamic Programming | $O(min(2^n, nW))$ |
0-1 Knapsack Problem | Backtracking(depth-first search) | $\Theta(2^n)$ |
0-1 Knapsack Problem | Branch-and-Bound(breadth-first search) | $\Theta(2^n)$ |
Owing to the additional bound of nW , it may appear that the dynamic programming algorithm is superior. However, in backtracking algorithms the worst case gives little insight into how much checking is usually saved by backtracking. In general, the breadth-first search strategy has no advantage over a depth-first search(backtracking).
일반적으로 0-1 배낭 문제에 대해 너비우선탐색의 효율이 깊이우선탐색(백트래킹)보다 더 좋다고 단정지을 순 없다. 깊이우선탐색과 너비우선탐색 중 무엇이 더 효율적이냐에 대한 답은 문제마다 다르기 때문이다.
nW 덕분에 동적계획 알고리즘으로 문제를 해결하는게 더 좋은 것처럼 보일 수 있다. 그러나 백트래킹, 너비우선탐색 알고리즘에서 최악의 경우를 따지면 실제 방문하는 노드의 수를 얼마나 절약했는지 반영이 안되므로, 알고리즘의 상대적인 효율을 이론적으로 분석하기는 어렵다.